#include "ExD3DWidget.h"
#include "QMessageBox"

ExD3DWidget::ExD3DWidget(QWidget *parent)
	: QWidget(parent), MsaaQulity(0), Enable4xMsaa(false)
{
	ui.setupUi(this);

	//设置窗口属性，关键步骤，否则D3D绘制出问题
	setAttribute(Qt::WA_PaintOnScreen, true);
	setAttribute(Qt::WA_NativeWindow, true);

	//不显示标题
	setWindowFlags(Qt::FramelessWindowHint);
}

ExD3DWidget::~ExD3DWidget()
{
	// 恢复所有默认设定
	if (D3dImmediateContext)
		D3dImmediateContext->ClearState();
}

bool ExD3DWidget::InitDirect3D()
{
	HRESULT Result = S_OK;

	/*   创建D3D3设备和上下文    */
	UINT CreateDeviceFlags = 0;
#if defined(DEBUG) || defined(_DEBUG)
	CreateDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

	//驱动类型数组
	D3D_DRIVER_TYPE DriverTypes[] =
	{
		D3D_DRIVER_TYPE_HARDWARE,
		D3D_DRIVER_TYPE_WARP,
		D3D_DRIVER_TYPE_REFERENCE,
	};
	UINT NumDriverTypes = ARRAYSIZE(DriverTypes);

	//特性等级数组
	D3D_FEATURE_LEVEL FeatureLevels[] =
	{
		D3D_FEATURE_LEVEL_11_1,
		D3D_FEATURE_LEVEL_11_0,
	};
	UINT NumFeatureLevels = ARRAYSIZE(FeatureLevels);

	//创建设备和上下文, 创建的同时获取驱动类型和等级特性
	D3D_DRIVER_TYPE D3dDriverType;
	D3D_FEATURE_LEVEL D3dFeatureLevel;
	//遍历驱动类型
	for (UINT DriverTypeIndex = 0; DriverTypeIndex < NumDriverTypes; ++DriverTypeIndex)
	{
		D3dDriverType = DriverTypes[DriverTypeIndex];
		Result = D3D11CreateDevice(nullptr, D3dDriverType, nullptr, CreateDeviceFlags, FeatureLevels, NumFeatureLevels, D3D11_SDK_VERSION, D3dDevice.GetAddressOf(), &D3dFeatureLevel, D3dImmediateContext.GetAddressOf());

		if (Result == E_INVALIDARG)
		{
			//Direct3D 11.0 的API不承认D3D_FEATURE_LEVEL_11_1，所以我们需要尝试特性等级11.0以及以下的版本
			Result = D3D11CreateDevice(nullptr, D3dDriverType, nullptr, CreateDeviceFlags, &FeatureLevels[1], NumFeatureLevels - 1, D3D11_SDK_VERSION, D3dDevice.GetAddressOf(), &D3dFeatureLevel, D3dImmediateContext.GetAddressOf());
		}

		if (SUCCEEDED(Result))
			break;
	}

	if (FAILED(Result))
	{
		QMessageBox::warning(this, "warning", "D3D11CreateDevice Failed.");
		return false;
	}

	// 检测是否支持特性等级11.0或11.1
	if (D3dFeatureLevel != D3D_FEATURE_LEVEL_11_0 && D3dFeatureLevel != D3D_FEATURE_LEVEL_11_1)
	{
		QMessageBox::warning(this, "warning", "Direct3D Feature Level 11 unsupported.");
		return false;
	}

	//检测MSAA支持的质量等级
	D3dDevice->CheckMultisampleQualityLevels(DXGI_FORMAT_B8G8R8A8_UNORM, 4, &MsaaQulity);
	assert(MsaaQulity > 0);



	/*     生成交换链       */
	ComPtr<IDXGIDevice> DxgiDevice = nullptr;
	ComPtr<IDXGIAdapter> DxgiAdapter = nullptr;
	ComPtr<IDXGIFactory1> DxgiFactory1 = nullptr; // D3D11.0(包含DXGI1.1)的接口类
	ComPtr<IDXGIFactory2> DxgiFactory2 = nullptr; // D3D11.1(包含DXGI1.2)特有的接口类

	// 为了正确创建 DXGI交换链，首先我们需要获取创建 D3D设备 的 DXGI工厂，否则会引发报错：
	// "IDXGIFactory::CreateSwapChain: This function is being called with a device from a different IDXGIFactory."
	HR(D3dDevice.As(&DxgiDevice));
	HR(DxgiDevice->GetAdapter(DxgiAdapter.GetAddressOf()));
	HR(DxgiAdapter->GetParent(__uuidof(IDXGIFactory1), reinterpret_cast<void**>(DxgiFactory1.GetAddressOf())));

	// 查看该对象是否包含IDXGIFactory2接口
	Result = DxgiFactory1.As(&DxgiFactory2);
	// 如果包含，则说明支持D3D11.1
	if (Result == S_OK)
	{
		// 在上面使用DxgiFactory2不为空判断到支持DX11.1后, 才把设备和上下文转换成DX11.1
		HR(D3dDevice.As(&D3dDevice1));
		HR(D3dImmediateContext.As(&D3dImmediateContext1));
		// 填充交换链形容结构体
		DXGI_SWAP_CHAIN_DESC1 ChainDese;
		ZeroMemory(&ChainDese, sizeof(ChainDese));
		ChainDese.Width = width();
		ChainDese.Height = height();
		ChainDese.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
		// 开启4倍多重采样
		if (Enable4xMsaa)
		{
			ChainDese.SampleDesc.Count = 4;
			ChainDese.SampleDesc.Quality = MsaaQulity - 1;
		}
		else
		{
			ChainDese.SampleDesc.Count = 1;
			ChainDese.SampleDesc.Quality = 0;
		}
		ChainDese.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
		ChainDese.BufferCount = 1;
		ChainDese.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
		ChainDese.Flags = 0;

		//更新全屏配置信息
		DXGI_SWAP_CHAIN_FULLSCREEN_DESC FullscreenDesc;
		FullscreenDesc.RefreshRate.Numerator = 60;
		FullscreenDesc.RefreshRate.Denominator = 1;
		FullscreenDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
		FullscreenDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
		//是否窗口显示, 这里选true
		FullscreenDesc.Windowed = TRUE;
		//为当前窗口创建交换链
		HR(DxgiFactory2->CreateSwapChainForHwnd(D3dDevice1.Get(), (HWND)winId(), &ChainDese, &FullscreenDesc, nullptr, D3dSwapChain1.GetAddressOf()));
		HR(D3dSwapChain1.As(&D3dSwapChain));
	}
	else
	{
		//DX11.0创建交换链的所有配置都在这里进行
		DXGI_SWAP_CHAIN_DESC ChainDese;
		ZeroMemory(&ChainDese, sizeof(ChainDese));
		ChainDese.BufferDesc.Width = width();
		ChainDese.BufferDesc.Height = height();
		ChainDese.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
		ChainDese.BufferDesc.RefreshRate.Numerator = 60;
		ChainDese.BufferDesc.RefreshRate.Denominator = 1;
		ChainDese.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
		ChainDese.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
		// 开启4倍多重采样
		if (Enable4xMsaa)
		{
			ChainDese.SampleDesc.Count = 4;
			ChainDese.SampleDesc.Quality = MsaaQulity - 1;
		}
		else
		{
			ChainDese.SampleDesc.Count = 1;
			ChainDese.SampleDesc.Quality = 0;
		}
		ChainDese.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
		ChainDese.BufferCount = 1;
		ChainDese.OutputWindow = (HWND)winId();
		ChainDese.Windowed = TRUE;
		ChainDese.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
		ChainDese.Flags = 0;
		HR(DxgiFactory1->CreateSwapChain(D3dDevice.Get(), &ChainDese, D3dSwapChain.GetAddressOf()));
	}

	//禁止alt+enter全屏
	DxgiFactory1->MakeWindowAssociation((HWND)winId(), DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_WINDOW_CHANGES);

	//buffer大小调整逻辑在这里
	OnResize();

}

void ExD3DWidget::OnResize()
{
	/*     绑定交换链的后备缓冲区到目标视图     */

	assert(D3dImmediateContext);
	assert(D3dDevice);
	assert(D3dSwapChain);

	if (D3dDevice1 != nullptr)
	{
		assert(D3dImmediateContext1);
		assert(D3dDevice1);
		assert(D3dSwapChain1);
	}

	//释放渲染管线输出用到的相关资源
	RenderTargetView.Reset();

	// 重设交换链并且重新创建渲染目标视图, 显示到屏幕的数据是在交换链设备缓冲区中的, 通过GetBuffer可以获取, 获取之后使用设备的CreateRenderTargetView方法使用缓冲区数据生成目标视图
	ComPtr<ID3D11Texture2D> BackBuffer;
	//根据窗口大小重置后备缓冲区的大小
	HR(D3dSwapChain->ResizeBuffers(1, width(), height(), DXGI_FORMAT_R8G8B8A8_UNORM, 0));
	//获取后备缓冲区
	HR(D3dSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(BackBuffer.GetAddressOf())));
	//绑定缓冲区给目标视图
	HR(D3dDevice->CreateRenderTargetView(BackBuffer.Get(), nullptr, RenderTargetView.GetAddressOf()));

	//释放后备缓冲区
	BackBuffer.Reset();

	/*      绑定深度模板缓冲区到视图      */

	//释放深度模板缓冲区与视图
	DepthStencilView.Reset();
	DepthStencilBuffer.Reset();

	//深度模板缓冲区配置信息
	D3D11_TEXTURE2D_DESC DepthStencilDesc;
	DepthStencilDesc.Width = width();
	DepthStencilDesc.Height = height();
	DepthStencilDesc.MipLevels = 1;
	DepthStencilDesc.ArraySize = 1;
	DepthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;

	//是否使用 4x MSAA
	if (Enable4xMsaa)
	{
		DepthStencilDesc.SampleDesc.Count = 4;
		DepthStencilDesc.SampleDesc.Quality = MsaaQulity - 1;
	}
	else
	{
		DepthStencilDesc.SampleDesc.Count = 1;
		DepthStencilDesc.SampleDesc.Quality = 0;
	}

	DepthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
	DepthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
	DepthStencilDesc.CPUAccessFlags = 0;
	DepthStencilDesc.MiscFlags = 0;

	// 创建深度缓冲区以及深度模板视图, 先创建2D纹理
	HR(D3dDevice->CreateTexture2D(&DepthStencilDesc, nullptr, DepthStencilBuffer.GetAddressOf()));
	// 创建深度/模板视图, 将创建好的2D纹理绑定到新建的深度/模板视图
	HR(D3dDevice->CreateDepthStencilView(DepthStencilBuffer.Get(), nullptr, DepthStencilView.GetAddressOf()));

	// 将渲染目标视图和深度/模板缓冲区结合到管线
	D3dImmediateContext->OMSetRenderTargets(1, RenderTargetView.GetAddressOf(), DepthStencilView.Get());

	// 设置视口变换
	ScreenViewport.TopLeftX = 0;
	ScreenViewport.TopLeftY = 0;
	ScreenViewport.Width = static_cast<float>(width());
	ScreenViewport.Height = static_cast<float>(height());
	ScreenViewport.MinDepth = 0.0f;
	ScreenViewport.MaxDepth = 1.0f;

	D3dImmediateContext->RSSetViewports(1, &ScreenViewport);
}

ID3D11Device* ExD3DWidget::GetD3dDevice()
{
	if (D3dDevice1 != nullptr)
		return D3dDevice1.Get();
	if (D3dDevice != nullptr)
		return D3dDevice.Get();
	return nullptr;
}

ID3D11DeviceContext* ExD3DWidget::GetD3dImmediateContext()
{
	if (D3dImmediateContext1 != nullptr)
		return D3dImmediateContext1.Get();
	if (D3dImmediateContext != nullptr)
		return D3dImmediateContext.Get();
	return nullptr;
}

IDXGISwapChain* ExD3DWidget::GetSwapChain()
{
	if (D3dSwapChain1 != nullptr)
		return D3dSwapChain1.Get();
	if (D3dSwapChain != nullptr)
		return D3dSwapChain.Get();
	return nullptr;
}

ID3D11RenderTargetView* ExD3DWidget::GetRenderTargetView()
{
	return RenderTargetView.Get();
}

ID3D11DepthStencilView* ExD3DWidget::GetDepthStencilView()
{
	return DepthStencilView.Get();
}
